/* $Id: sysi86.c,v 1.12 2001/11/24 15:09:22 hch Exp $
 * sysi86.c - ix86 spcific sysv syscalls
 *
 * Copyright 1994,1995	Mike Jagdis <jaggy@purplet.demon.co.uk>
 */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/personality.h>
#include <linux/ptrace.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/sys.h>
#include <linux/syscall.h>

#include <asm/uaccess.h>
#include <asm/system.h>

#include <abi/util/trace.h>


/* The sysi86() call is used for machine specific functions. Only the
 * most basic are implemented here.
 */
#define SI86SWPI        1       /* General swap functions. */
#define SI86SYM         2       /* Get symbol table */
#define SI86CONF        4       /* Get configuration table */
#define SI86BOOT        5       /* Get timestamp and name of program
				 * booted.
				 */
#define SI86DMM         7       /* Double-map data segment for
				 * read/write/execute support
				 */
#define SI86AUTO        9       /* Was an auto-config boot done? */
#define SI86EDT         10      /* Copy contents of EDT to user */
#define SI86SWAP        12      /* Declare swap space */
#define SI86FPHW        40      /* what (if any?) floating-point hardware */
#  define FP_NO		0	/* No fp at all */
#  define FP_SW		1	/* using emulator */
#  define FP_HW		2	/* using hardware */
#  define FP_287	2	/* using a 287 */
#  define FP_387	3	/* using a 387 */
#define GRNON		52	/* set green light to solid on state */
#define GRNFLASH	53	/* start green light flashing */
#define STIME 		54	/* set internal time */
#define SETNAME		56	/* rename the system */
#define RNVR 		58	/* read NVRAM */
#define WNVR 		59	/* write NVRAM */
#define RTODC		60	/* read time of day clock */
#define CHKSER		61	/* check soft serial number */
#define SI86NVPRT       62      /* print an xtra_nvr structure */
#define SANUPD		63	/* sanity update of kernel buffers */
#define SI86KSTR        64      /* make a copy of a kernel string */
#define SI86MEM         65      /* return the memory size of system */
#define SI86TODEMON     66      /* Transfer control to firmware.        */
#define SI86CCDEMON     67      /* Control character access to demon.   */
#define SI86CACHE       68      /* Turn cache on and off.               */
#define SI86DELMEM      69      /* Delete available memory for testing. */
#define SI86ADDMEM      70      /* Add back deleted memory.             */
/* 71 through 74 reserved for VPIX */
#define SI86V86         71      /* V86 system calls (see v86.h)         */
#define	SI86SLTIME	72	/* Set local time correction		*/
#define SI86DSCR        75      /* Set a segment or gate descriptor     */
#define RDUBLK		76	/* Read U Block */
/* #ifdef MERGE386 */
/* NFA entry point */
#define SI86NFA         77      /* make nfa_sys system call */
#define SI86VM86	81
#define SI86VMENABLE	82
/* #endif MERGE386 */
#define SI86VM86        81
#define SI86VMENABLE    82
#define SI86LIMUSER     91      /* liscense interface */
#define SI86RDID        92      /* ROM BIOS Machid ID */
#define SI86RDBOOT      93      /* Bootable Non-SCSI Hard Disk */
/* Merged Product defines */
#define SI86SHFIL	100	/* map a file into addr space of a proc */
#define SI86PCHRGN	101	/* make globally visible change to a region */
#define SI86BADVISE	102	/* badvise subcommand - see below for   */
				/*	badvise subfunction definitions */
#define SI86SHRGN	103	/* enable/disable XENIX small model shared */
				/*	data context switching		   */ 
#define	SI86CHIDT	104	/* set user level int 0xf0, ... 0xff handlers */
#define	SI86EMULRDA	105	/* remove special emulator read access	*/
#define SI86GETPIPE	106	/* return the pipe filesystem */
#define SI86SETPIPE	107	/* set the pipe filesystem */
#define SI86SETPIPE_NM	108	/* set the pipe filesystem -non mountable */
#define SI86GETNPIPE	109	/* get # of pipe filesystems */
#define SI86GETPIPE_ALL	110	/* get data on all of pipe filesystems */
#define SI86POPPIPE	111	/* pop pipe file system off stack */
#define SI86APM		112	/* get APM information passed by boot(HW) */
#define SI86TIMECHG	113	/* get time before/after last timechange */
#define SI86GETFEATURES	114	/* get os features vector */

/* The SI86BADVISE command is used to set Xenix behaviour. */
#define SI86B_SET		0x0100	/* Set badvise bits */
#define SI86B_GET		0x0200	/* Get badvise bits */	
#define	SI86B_LOCK		0x0001	/* XENIX advisory locking bit */
#define SI86B_PRE_SV		0x0008	/* follow pre-System V x.out behavior */
#define SI86B_XOUT		0x0010 	/* follow XENIX x.out behavior */
#define SI86B_XSDSWTCH		0x0080	/* XENIX small model shared data */
					/* context switching enabled */


/*
 *  The SI86DSCR subcommand of the sysi86() system call
 *  sets a segment or gate descriptor in the kernel.
 *  The following descriptor types are accepted:
 *    - executable and data segments in the LDT at DPL 3
 *    - a call gate in the GDT at DPL 3 that points to a segment in the LDT
 *  The request structure declared below is used to pass the values
 *  to be placed in the descriptor.  A pointer to the structure is
 *  passed as the second argument of the system call.
 *  If acc1 is zero, the descriptor is cleared.
 */

/* request structure passed by user */
struct ssd {
        unsigned int sel;   /* descriptor selector */
        unsigned int bo;    /* segment base or gate offset */
        unsigned int ls;    /* segment limit or gate selector */
        unsigned int acc1;  /* access byte 5 */
	unsigned int acc2;  /* access bits in byte 6 or gate count */
};


int
do_sysi86(struct pt_regs *regp, int cmd)
{
	int			error;

	switch (cmd) {
	case SI86FPHW:
		{
			u_long	*np;

			/*
			 * If we remove the 'static' from the definition
			 * of fpu_error in linux/init/main.c we can tell
			 * whether we are using hardware or software at
			 * least. For now let's lie...
			 * (actually SCO Unix 3.4 gives me -1...)
			 */
			error = get_user((u_long)np, ((u_long *)regp->esp) + 2);
			if (error)
				return (error);
			return put_user(FP_387, np);
		}
	case STIME:
		/*
		 * Set the system time. The argument is a long,
		 * sys_stime() expects a pointer to a long...
		 *
		 *	*shudder*
		 */
		return sys_stime((int *)(((u_long *)regp->esp) + 2));
	case SETNAME:
		{
			/*
			 * The name is required to be string of no more
			 * than 7 characters. We don't get passed the
			 * length so we are depending upon the current
			 * implementation of sys_sethostname() here.
			 */
			char	*cp;

			error = get_user((u_long)cp, ((u_long *)regp->esp) + 2);
			if (error)
				return error;
			return sys_sethostname(cp, 7);
		}
	case SI86MEM:
		{
			/*
			 * Returns the size of physical memory.
			 */
			struct sysinfo	i;

			si_meminfo(&i);
			return (i.totalram << PAGE_SHIFT);
		}
#if defined(CONFIG_ABI_SCO) || defined(CONFIG_ABI_SCO_MODULE)
	case SI86GETFEATURES:
		{
			char	*vec;
			int	len, used;

#if 0
			if (!personality(PER_SCOSVR3)) {
				current->personality &= ~PER_MASK;
				current->personality |= PER_SCOSVR3;
			}
#endif

			error = get_user((u_long)vec, ((u_long *)regp->esp) + 2);
			if (error)
				return (error);
			error = get_user(len, ((u_long *)regp->esp) + 3);
			if (error)
				return (error);
			/*
			 * No, I don't know what these feature flags actually
			 * _mean_. This vector just matches SCO OS 5.0.0.
			 */
			used = len > 12 ? 12 : len;
			return copy_to_user(vec,
		"\001\001\001\001\001\001\001\001\002\001\001\001",
					used) ? -EFAULT : used;
		}
#endif
	case SI86DSCR:
		{
			struct ssd	buf;
			u_long		ssdptr;

			error = get_user(ssdptr, ((u_long *)regp->esp) + 2);
			if (error)
				return (error);
			error = copy_from_user(&buf, (void *)ssdptr, sizeof(struct ssd));
			if (error)
				return (error);

			printk("SI86DSCR(%x,%x,%x,%x,%x)\n",
					buf.sel, buf.bo, buf.ls,
					buf.acc1, buf.acc2);
			return -EINVAL;
		}
	}

	abi_trace(ABI_TRACE_API, "unsupported sysi86 call %d\n", cmd);
	return -EINVAL;
}

int
svr4_sysi86(struct pt_regs *regp)
{
	int			error, cmd;

	error = get_user(cmd, ((u_long *) regp->esp) + 1);
	if (error)
		return (error);
	return do_sysi86(regp, cmd);
}

